package com.salinity.kun.helper;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import com.salinity.kun.algorithm.harmonic.ConstantParamHelper;
import com.salinity.kun.algorithm.harmonic.GaussianElimination;
import com.salinity.kun.algorithm.harmonic.Harmonic;
import com.salinity.kun.algorithm.harmonic.InitialPhase;
import com.salinity.kun.algorithm.harmonic.InterFactorAndCorAngle;
import com.salinity.kun.algorithm.harmonic.TideSymbol;
import com.salinity.kun.model.StationDateData;
import com.salinity.kun.util.PathUtil;

import Jama.Matrix;

public class HarmonicHelper {

	private InitialPhase initialPhase = new InitialPhase(null);

	public double[][] normalizeMatrix(double[][] m) {
		return Harmonic.normalizeMatrix(m);
	}

	/**
	 * 获取角速度， 单位为弧度
	 * 
	 * @param tideSymbol
	 * @return
	 */
	public double getAngularVelocity(TideSymbol tideSymbol) {
		return ConstantParamHelper.getAngularVelocity().getAngel(tideSymbol) * Math.PI / 180;
	}

	/**
	 * 获取初相位
	 * 
	 * @param tideSymbol
	 * @param date
	 * @return
	 */
	public double getInitialPhase(TideSymbol tideSymbol, Date date) {
		initialPhase.setTideSymbol(tideSymbol);
		initialPhase.setTime(date);
		return initialPhase.getV0();

	}

	/**
	 * 根据测量数据来生成矩阵
	 * 
	 * @param dataList
	 * @param t
	 *            单位为：分钟
	 * @return 结果分别为:ab, 第二个为 fab
	 */
	public List<double[][]> getMatrix_V2(List<StationDateData> dataList, double deltaT,
			List<TideSymbol> tideSymbolList) {
		Harmonic harmonic = new Harmonic(dataList, deltaT);
		harmonic.setTideSymbolList(tideSymbolList);

		List<double[][]> rsltList = harmonic.getMatrix_v2();

		return rsltList;
	}

	/**
	 * 周期为 [-T/2,T/2]
	 * 
	 * @param dataList
	 * @param deltaT
	 * @param tideSymbolList
	 * @return 结果分别为:a,fa, 第二个为b，fb
	 */

	public List<double[][]> getMatrix_V1(List<StationDateData> dataList, double deltaT, List<TideSymbol> tideSymbolList,
			double offset_t) {
		Harmonic harmonic = new Harmonic(dataList, deltaT);
		harmonic.setTideSymbolList(tideSymbolList);

		List<double[][]> rsltList = harmonic.getMatrix_v1(offset_t);

		return rsltList;
	}

	public List<double[][]> getMatrix_V1(List<StationDateData> dataList, double deltaT,
			List<TideSymbol> tideSymbolList) {
		Harmonic harmonic = new Harmonic(dataList, deltaT);
		harmonic.setTideSymbolList(tideSymbolList);

		List<double[][]> rsltList = harmonic.getMatrix_v1();

		return rsltList;
	}

	public double[] getMatrixRslt(double[][] matrix) {

		return GaussianElimination.columnMainElimination(matrix);
	}

	public Matrix getMatrixRslt(double[][] m, double[][] fm, double offset) {
		return Harmonic.calParams(m, fm, offset);
	}

	public Matrix getMatrixRslt(double[][] m, double[][] fm, double threshold, double offset) {
		return Harmonic.calParams(m, fm, threshold, offset);
	}

	/**
	 * 粗预测，不考虑迟角和初相位
	 */
	public double predictionTest(List<TideSymbol> tideSymbolList, double[] ax, double[] bx, double startTime,
			double preTime) {

		double w, rslt = 0.0, deltaT = preTime - startTime;

		rslt += ax[0];
		int i, n = tideSymbolList.size();

		for (i = 0; i < n; i++) {
			w = getAngularVelocity(tideSymbolList.get(i));
			rslt += ax[i + 1] * Math.cos(w * deltaT);
			rslt += bx[i] * Math.sin(w * deltaT);

		}

		return rslt;
	}

	/**
	 * 
	 * 获取调和常数序列
	 * 
	 * H = R/f || g = V0 + u + theta|| theta
	 * 
	 * R=sqrt(a*a + b*b)
	 * 
	 * 
	 * @param tideSymbolList
	 * @param ax
	 * @param bx
	 * @param midDate
	 * @return H,g
	 * @throws ParseException
	 * 
	 * 
	 * 
	 */
	public double[][] getInterFactorsAndCorAngle(List<TideSymbol> tideSymbolList, double[] ax, double[] bx,
			Date startDate, Date midDate) throws ParseException {

		double Ri, Hi, fi, u, fiAndU[], V0, theta, rslt[][];

		rslt = new double[3][bx.length];
		InitialPhase initialPhase = new InitialPhase();

		initialPhase.setTime(startDate);
		InterFactorAndCorAngle IFACAngle = new InterFactorAndCorAngle(midDate);

		for (int i = 1; i < ax.length; i++) {
			fiAndU = IFACAngle.getValue(tideSymbolList.get(i - 1));
			Ri = Math.sqrt(ax[i] * ax[i] + bx[i - 1] * bx[i - 1]);
			fi = fiAndU[0];
			u = fiAndU[1];

			// 计算H
			Hi = Ri / fi;
			rslt[0][i - 1] = Ri;

			// 计算g
			initialPhase.setTideSymbol(tideSymbolList.get(i - 1));
			V0 = initialPhase.getV0();

			theta = getCorrectionAngle(ax[i], bx[i - 1]) * 180 / Math.PI;
			rslt[1][i - 1] = (V0 + u + theta) % 360f;
			rslt[2][i - 1] = theta % 360f;
		}

		return rslt;
	}

	/**
	 * 考虑象限，求取theta值
	 * 
	 * @param a
	 * @param b
	 * @return 弧度
	 */
	public double getCorrectionAngle(double a, double b) {

		if (a > 0) {
			if (b > 0) {
				return Math.atan(Math.abs(b * 1.0f / a));
			} else {
				return 2 * Math.PI - Math.atan(Math.abs(b * 1.0f / a));
			}
		} else {
			if (b > 0) {
				return Math.PI - Math.atan(Math.abs(b * 1.0f / a));
			} else {
				return Math.PI + Math.atan(Math.abs(b * 1.0f / a));
			}
		}
	}

	public void saveParamsAsFile(double[] ax, double[] bx, Path outputPath) throws IOException {

		PathUtil.preparePath(outputPath);
		try (BufferedWriter bw = Files.newBufferedWriter(outputPath)) {
			bw.write(paramsAsString(ax, bx).toString());
		}
	}

	/**
	 * 保存调和常数 h,g
	 * 
	 * @param tideSymbolList
	 * @param params
	 *            第一个表示H,第二个表示g
	 * @param outputPath
	 * @throws IOException
	 */
	public void saveHarmonicParams(List<TideSymbol> tideSymbolList, double[][] params, Path outputPath)
			throws IOException {
		PathUtil.preparePath(outputPath);

		int i, n = tideSymbolList.size();

		try (BufferedWriter bw = Files.newBufferedWriter(outputPath)) {
			bw.write("H,g\n");
			for (i = 0; i < n; i++) {
				bw.write(params[0][i] + "," + params[1][i] + ",\n");
			}
		}
	}

	public StringBuilder paramsAsString(double[] ax, double[] bx) {

		StringBuilder sb = new StringBuilder();
		sb.append("i,ax,bx,Hi").append("\n");
		DecimalFormat df = ConstantParamHelper.getDefaultDecimalFormat();
		int i, n = ax.length;
		double Hi;
		sb.append("0,").append(df.format(ax[0])).append("\n");
		for (i = 1; i < n; i++) {
			Hi = Math.sqrt(ax[i] * ax[i] + bx[i - 1] * bx[i - 1]);
			sb.append(i + ",").append(df.format(ax[i])).append(",").append(df.format(bx[i - 1])).append(",")
					.append(df.format(Hi)).append("\n");
		}
		return sb;
	}

	public void findOffsetVal(double[][] ab, double[][] fab, double range, int divCount) {

		double interval = range / divCount;
		double offset = 0.0;
		double[] tempParams;
		int i, j;
		for (i = 0; i < divCount + 1; i++) {

			tempParams = getMatrixRslt(ab, fab, offset).transpose().getArray()[0];
			System.out.printf("%.6f\t", tempParams[0]);
			for (j = 1; j + 1 < tempParams.length; j += 2) {
				System.out.printf("%.6f\t",
						Math.sqrt(tempParams[j] * tempParams[j] + tempParams[j + 1] * tempParams[j + 1]));
			}

			System.out.println();
			offset += interval;
		}
	}

	public void printParams(double[] ax, double[] bx) {

		System.out.println(paramsAsString(ax, bx).toString());
	}

	public double[][] matrixPlusNum(double[][] m, double num) {

		int i, j;
		double[][] rsltM = new double[m.length][m[0].length];
		for (i = 0; i < m.length; i++) {
			for (j = 0; j < m[0].length; j++) {
				rsltM[i][j] = m[i][j] + num;
			}
		}

		return rsltM;
	}

	/**
	 * 只对对角线元素进行计算
	 * 
	 * @param m
	 * @param num
	 * @return
	 */
	public double[][] matrixDiagonalPlusNum(double[][] m, double num) {

		int i, j;
		double[][] rsltM = new double[m.length][m[0].length];
		for (i = 0; i < m.length; i++) {
			for (j = 0; j < m[0].length; j++) {
				rsltM[i][j] = m[i][j];
			}
			rsltM[i][i] += num;
		}

		return rsltM;
	}

	/**
	 * 
	 * replace by 'checkRslt(List<StationDateData> dataList, double deltaT, double[]
	 * ax,double[] bx)'
	 * 
	 * @param dataList
	 * @param deltaT
	 *            单位为小时
	 * @param params(a
	 *            在前，b 在后)
	 * @return
	 */
	@Deprecated
	public double[] checkRslt(List<StationDateData> dataList, double deltaT, double[] params) {

		int n = dataList.size();
		Harmonic harmonic = new Harmonic(dataList, deltaT);

		double[] calRslt = new double[n];
		double t0 = (n - 1) * deltaT / 2, ti;
		for (int i = 0; i < n; i++) {
			ti = -t0 + deltaT * i;
			calRslt[i] = harmonic.checkRslt(params, ti);
		}

		return calRslt;
	}

	/**
	 * 周期为[0,T]
	 * 
	 * @param dataList
	 * @param deltaT
	 *            单位为：小时
	 * @param ax
	 * @param bx
	 * @return
	 */
	public double[] checkRslt_V2(List<StationDateData> dataList, double deltaT, double[] ax, double[] bx) {

		int n = dataList.size();
		Harmonic harmonic = new Harmonic(dataList, deltaT);

		double[] calRslt = new double[n];
		double t0 = 0.0, ti;
		for (int i = 0; i < n; i++) {
			ti = t0 + deltaT * i;
			calRslt[i] = harmonic.checkRslt(ax, bx, ti);
		}
		return calRslt;
	}

	public double[] checkRslt_V1(List<StationDateData> dataList, double deltaT, double[] ax, double[] bx) {

		int n = dataList.size();
		Harmonic harmonic = new Harmonic(dataList, deltaT);

		double[] calRslt = new double[n];
		double t0 = (n - 1) * deltaT / 2, ti;
		for (int i = 0; i < n; i++) {
			ti = -t0 + deltaT * i;
			calRslt[i] = harmonic.checkRslt(ax, bx, ti);
		}

		return calRslt;
	}


	/**
	 * 
	 * @param dataList
	 * @param deltaT
	 * @param ax
	 * @param bx
	 * @param outputPath
	 * @return
	 * @throws IOException
	 */

	public double[] checkRsltAndSave_V1(List<StationDateData> dataList, double deltaT, double[] ax, double[] bx,
			Path outputPath) throws IOException {
		double[] checkRslt = checkRslt_V1(dataList, deltaT, ax, bx);
		saveToFile(dataList, checkRslt, outputPath);
		return checkRslt;
	}

	public double[] checkRsltAndSave_V2(List<StationDateData> dataList, double deltaT, double[] ax, double[] bx,
			Path outputPath) throws IOException {
		double[] checkRslt = checkRslt_V2(dataList, deltaT, ax, bx);
		saveToFile(dataList, checkRslt, outputPath);
		return checkRslt;
	}

	public void saveToFile(List<StationDateData> dataList, double[] checkRslt, Path outputPath) throws IOException {
		PathUtil.preparePath(outputPath);
		DecimalFormat df = new DecimalFormat("#.0000");
		int n = dataList.size(), i;
		try (BufferedWriter bw = Files.newBufferedWriter(outputPath, StandardOpenOption.WRITE)) {
			bw.write("date,Hp,Hr,");
			bw.write("n," + n);
			bw.newLine();
			for (i = 0; i < n; i++) {
				bw.write(dataList.get(i).getDate());
				bw.write(",");
				bw.write(df.format(checkRslt[i]) + "," + df.format(dataList.get(i).getValue()));
				bw.newLine();
			}
		}
	}

	/**
	 * 
	 * replace by 'checkRsltAndSave(List<StationDateData> dataList, double deltaT,
	 * double[] ax,double[]bx, Path outputPath)'
	 * 
	 * @param dataList
	 * @param deltaT
	 * @param params
	 * @param outputPath
	 * @return
	 * @throws IOException
	 */
	@Deprecated
	public double[] checkRsltAndSave(List<StationDateData> dataList, double deltaT, double[] params, Path outputPath)
			throws IOException {

		double[] checkRslt = checkRslt(dataList, deltaT, params);

		PathUtil.preparePath(outputPath);
		DecimalFormat df = new DecimalFormat("#.0000");
		int n = dataList.size(), i;
		try (BufferedWriter bw = Files.newBufferedWriter(outputPath, StandardOpenOption.WRITE)) {
			bw.write("date,raw,cal,");
			bw.write("n," + n);
			bw.newLine();
			for (i = 0; i < n; i++) {
				bw.write(dataList.get(i).getDate());
				bw.write(",");
				bw.write(df.format(dataList.get(i).getValue()) + "," + df.format(checkRslt[i]));
				bw.newLine();
			}
		}

		return checkRslt;
	}

}
